เชี่ยวชาญไฟล์ประกาศไทป์สคริปต์ (.d.ts) เพื่อปลดล็อกความปลอดภัยของประเภทและการเติมข้ออัตโนมัติสำหรับไลบรารี JavaScript ใดๆ เรียนรู้วิธีใช้ @types สร้างคำจำกัดความของคุณเอง และจัดการโค้ดของบุคคลที่สามอย่างมืออาชีพ
ปลดล็อกระบบนิเวศ JavaScript: เจาะลึกไฟล์ประกาศ TypeScript
TypeScript ได้ปฏิวัติการพัฒนาเว็บสมัยใหม่โดยนำการกำหนดประเภทแบบคงที่มาสู่โลกแบบไดนามิกของ JavaScript ความปลอดภัยของประเภทนี้มอบประโยชน์ที่น่าทึ่ง: การจับข้อผิดพลาดในขณะคอมไพล์ การเปิดใช้งานการเติมข้ออัตโนมัติที่มีประสิทธิภาพในโปรแกรมแก้ไข และทำให้ฐานโค้ดขนาดใหญ่สามารถบำรุงรักษาได้อย่างมาก อย่างไรก็ตาม ความท้าทายหลักเกิดขึ้นเมื่อเราต้องการใช้ระบบนิเวศที่กว้างขวางของไลบรารี JavaScript ที่มีอยู่ ซึ่งส่วนใหญ่ไม่ได้เขียนด้วย TypeScript โค้ด TypeScript ที่กำหนดประเภทอย่างเข้มงวดของเราจะเข้าใจโครงสร้าง ฟังก์ชัน และตัวแปรจากไลบรารี JavaScript ที่ไม่ได้กำหนดประเภทได้อย่างไร?
คำตอบอยู่ที่ ไฟล์ประกาศ TypeScript ไฟล์เหล่านี้ ซึ่งสามารถระบุได้ด้วยนามสกุล .d.ts เป็นสะพานเชื่อมที่จำเป็นระหว่างโลกของ TypeScript และ JavaScript พวกมันทำหน้าที่เป็นพิมพ์เขียวหรือสัญญา API ซึ่งอธิบายประเภทของไลบรารีของบุคคลที่สามโดยไม่มีการนำไปใช้จริง ในคู่มือฉบับสมบูรณ์นี้ เราจะสำรวจทุกสิ่งที่คุณจำเป็นต้องรู้เพื่อจัดการคำจำกัดความประเภทสำหรับไลบรารี JavaScript ใดๆ ในโปรเจกต์ TypeScript ของคุณได้อย่างมั่นใจ
ไฟล์ประกาศ TypeScript คืออะไรกันแน่?
ลองนึกภาพว่าคุณจ้างผู้รับเหมาที่พูดได้เพียงภาษาอื่น เพื่อทำงานกับพวกเขาอย่างมีประสิทธิภาพ คุณจะต้องมีนักแปลหรือชุดคำแนะนำโดยละเอียดในภาษาที่คุณทั้งคู่เข้าใจ ไฟล์ประกาศมีวัตถุประสงค์นี้สำหรับคอมไพเลอร์ TypeScript (ผู้รับเหมา)
ไฟล์ .d.ts มีเฉพาะข้อมูลประเภทเท่านั้น ซึ่งรวมถึง:
- ลายเซ็นสำหรับฟังก์ชันและเมธอด (ประเภทพารามิเตอร์ ประเภทส่งคืน)
- คำจำกัดความสำหรับตัวแปรและประเภทของพวกมัน
- อินเทอร์เฟซและนามแฝงประเภทสำหรับอ็อบเจกต์ที่ซับซ้อน
- คำจำกัดความคลาส รวมถึงคุณสมบัติและเมธอดของพวกมัน
- โครงสร้างเนมสเปซและโมดูล
สิ่งสำคัญคือไฟล์เหล่านี้ไม่มีโค้ดที่สามารถดำเนินการได้ พวกมันมีไว้สำหรับการวิเคราะห์แบบคงที่เท่านั้น เมื่อคุณนำเข้าไลบรารี JavaScript เช่น Lodash เข้าสู่โปรเจกต์ TypeScript ของคุณ คอมไพเลอร์จะมองหาไฟล์ประกาศที่สอดคล้องกัน หากพบ คอมไพเลอร์สามารถตรวจสอบโค้ดของคุณ ให้การเติมข้ออัตโนมัติอัจฉริยะ และตรวจสอบให้แน่ใจว่าคุณกำลังใช้ไลบรารีอย่างถูกต้อง หากไม่พบ คอมไพเลอร์จะแสดงข้อผิดพลาดเช่น: Could not find a declaration file for module 'lodash'.
เหตุใดไฟล์ประกาศจึงเป็นสิ่งจำเป็นสำหรับการพัฒนาอย่างมืออาชีพ
การใช้ไลบรารี JavaScript โดยไม่มีคำจำกัดความประเภทที่เหมาะสมในโปรเจกต์ TypeScript จะบ่อนทำลายเหตุผลของการใช้ TypeScript ตั้งแต่แรก พิจารณาสถานการณ์ง่ายๆ โดยใช้ไลบรารีอรรถประโยชน์ยอดนิยมอย่าง Lodash
โลกที่ไม่มีคำจำกัดความประเภท
หากไม่มีไฟล์ประกาศ TypeScript จะไม่ทราบว่า lodash คืออะไรหรือมีอะไรอยู่บ้าง เพื่อให้โค้ดคอมไพล์ได้ คุณอาจถูกล่อลวงให้ใช้การแก้ไขอย่างรวดเร็วเช่นนี้:
const _: any = require('lodash');
const users = [{ 'user': 'barney' }, { 'user': 'fred' }];
// การเติมข้ออัตโนมัติ? ไม่มีใครช่วยที่นี่
// การตรวจสอบประเภท? ไม่ใช่ 'username' เป็นคุณสมบัติที่ถูกต้องหรือไม่?
// คอมไพเลอร์อนุญาตสิ่งนี้ แต่อาจล้มเหลวขณะรันไทม์
_.find(users, { username: 'fred' });
ในกรณีนี้ ตัวแปร _ มีประเภทเป็น any ซึ่งบอก TypeScript ว่า "อย่าตรวจสอบอะไรก็ตามที่เกี่ยวข้องกับตัวแปรนี้" คุณจะสูญเสียประโยชน์ทั้งหมด: ไม่มีการเติมข้ออัตโนมัติ ไม่มีการตรวจสอบประเภทของอาร์กิวเมนต์ และไม่แน่ใจเกี่ยวกับประเภทส่งคืน นี่เป็นแหล่งเพาะพันธุ์ข้อผิดพลาดขณะรันไทม์
โลกที่มีคำจำกัดความประเภท
ตอนนี้ มาดูกันว่าเกิดอะไรขึ้นเมื่อเราจัดเตรียมไฟล์ประกาศที่จำเป็น หลังจากติดตั้งประเภท (ซึ่งเราจะกล่าวถึงต่อไป) ประสบการณ์จะเปลี่ยนไป:
import _ from 'lodash';
interface User {
user: string;
active?: boolean;
}
const users: User[] = [{ 'user': 'barney' }, { 'user': 'fred' }];
// 1. โปรแกรมแก้ไขให้การเติมข้ออัตโนมัติสำหรับ 'find' และฟังก์ชัน lodash อื่นๆ
// 2. การวางเมาส์เหนือ 'find' จะแสดงลายเซ็นและเอกสารฉบับเต็ม
// 3. TypeScript เห็นว่า `users` เป็นอาร์เรย์ของอ็อบเจกต์ `User`
// 4. TypeScript รู้ว่า predicate สำหรับ `find` บน `User[]` ควรเกี่ยวข้องกับ `user` หรือ `active`
// ถูกต้อง: TypeScript พอใจ
const fred = _.find(users, { user: 'fred' });
// ข้อผิดพลาด: TypeScript จับข้อผิดพลาดได้!
// คุณสมบัติ 'username' ไม่มีอยู่ในประเภท 'User'
const betty = _.find(users, { username: 'betty' });
ความแตกต่างนั้นแตกต่างกันอย่างมาก เราได้รับความปลอดภัยของประเภทเต็มรูปแบบ ประสบการณ์นักพัฒนาที่เหนือกว่าผ่านเครื่องมือ และการลดจำนวนข้อบกพร่องที่อาจเกิดขึ้นได้อย่างมาก นี่คือมาตรฐานระดับมืออาชีพสำหรับการทำงานกับ TypeScript
ลำดับชั้นของการค้นหาคำจำกัดความประเภท
ดังนั้น คุณจะได้รับไฟล์ .d.ts ที่มหัศจรรย์เหล่านี้สำหรับไลบรารีที่คุณชื่นชอบได้อย่างไร? มีกระบวนการที่เป็นที่ยอมรับซึ่งครอบคลุมสถานการณ์ส่วนใหญ่
ขั้นตอนที่ 1: ตรวจสอบว่าไลบรารีรวมประเภทของตนเองหรือไม่
สถานการณ์ที่ดีที่สุดคือเมื่อไลบรารีถูกเขียนด้วย TypeScript หรือผู้ดูแลได้จัดเตรียมไฟล์ประกาศอย่างเป็นทางการไว้ในแพ็กเกจเดียวกัน สิ่งนี้กำลังเป็นที่นิยมมากขึ้นสำหรับโปรเจกต์ที่ทันสมัยและได้รับการดูแลอย่างดี
วิธีตรวจสอบ:
- ติดตั้งไลบรารีตามปกติ:
npm install axios - มองเข้าไปในโฟลเดอร์ไลบรารีใน
node_modules/axiosคุณเห็นไฟล์.d.tsหรือไม่? - ตรวจสอบไฟล์
package.jsonของไลบรารีสำหรับฟิลด์"types"หรือ"typings"ฟิลด์นี้จะชี้ตรงไปยังไฟล์ประกาศหลัก ตัวอย่างเช่นpackage.jsonของ Axios มี:"types": "index.d.ts"
หากเป็นไปตามเงื่อนไขเหล่านี้ คุณก็เสร็จแล้ว! TypeScript จะค้นหาและใช้ประเภทที่รวมไว้เหล่านี้โดยอัตโนมัติ ไม่จำเป็นต้องดำเนินการใดๆ เพิ่มเติม
ขั้นตอนที่ 2: โครงการ DefinitelyTyped (@types)
สำหรับไลบรารี JavaScript จำนวนหลายพันรายการที่ไม่ได้รวมประเภทของตนเอง ชุมชน TypeScript ทั่วโลกได้สร้างแหล่งข้อมูลที่น่าทึ่ง: DefinitelyTyped
DefinitelyTyped เป็นที่เก็บส่วนกลางที่จัดการโดยชุมชนบน GitHub ซึ่งโฮสต์ไฟล์ประกาศคุณภาพสูงสำหรับแพ็กเกจ JavaScript จำนวนมหาศาล คำจำกัดความเหล่านี้จะถูกเผยแพร่ไปยังรีจิสทรี npm ภายใต้สโคป @types
วิธีใช้งาน:
หากไลบรารีเช่น lodash ไม่ได้รวมประเภทของตนเอง คุณเพียงแค่ติดตั้งแพ็กเกจ @types ที่สอดคล้องกันเป็น dependency สำหรับการพัฒนา:
npm install --save-dev @types/lodash
รูปแบบการตั้งชื่อนั้นเรียบง่ายและคาดเดาได้: สำหรับแพ็กเกจที่ชื่อ package-name ประเภทของมันเกือบจะอยู่ที่ @types/package-name เสมอ คุณสามารถค้นหาประเภทที่มีอยู่บนเว็บไซต์ npm หรือโดยตรงบน คลัง DefinitelyTyped
เหตุใดจึงต้องใช้ --save-dev? ไฟล์ประกาศจำเป็นเฉพาะในระหว่างการพัฒนาและการคอมไพล์เท่านั้น พวกมันไม่มีโค้ดขณะรันไทม์ ดังนั้นจึงไม่ควรรวมอยู่ใน bundle การผลิตขั้นสุดท้ายของคุณ การติดตั้งเป็น devDependency จะทำให้มั่นใจได้ถึงการแยกส่วนนี้
ขั้นตอนที่ 3: เมื่อไม่มีประเภท - การเขียนของคุณเอง
จะเกิดอะไรขึ้นหากคุณกำลังใช้ไลบรารีเก่า ไลบรารีเฉพาะ หรือไลบรารีส่วนตัวภายในที่ไม่ได้รวมประเภทและไม่อยู่ใน DefinitelyTyped? ในกรณีนี้ คุณต้องลงมือทำเองและสร้างไฟล์ประกาศของคุณเอง แม้ว่าสิ่งนี้อาจฟังดูน่ากลัว แต่คุณสามารถเริ่มต้นง่ายๆ และเพิ่มรายละเอียดตามความจำเป็น
การแก้ไขอย่างรวดเร็ว: การประกาศโมดูลสภาพแวดล้อมแบบย่อ
บางครั้งคุณเพียงต้องการให้โปรเจกต์ของคุณคอมไพล์ได้โดยไม่มีข้อผิดพลาดในขณะที่คุณกำลังพิจารณากลยุทธ์การกำหนดประเภทที่เหมาะสม คุณสามารถสร้างไฟล์ในโปรเจกต์ของคุณ (เช่น declarations.d.ts หรือ types/global.d.ts) และเพิ่มการประกาศแบบย่อ:
// ในไฟล์ .d.ts
declare module 'some-untyped-library';
นี่เป็นการบอก TypeScript ว่า "เชื่อฉันเถอะว่ามีโมดูลชื่อ 'some-untyped-library' ให้ถือว่าทุกอย่างที่นำเข้าจากโมดูลนั้นเป็นประเภท any" สิ่งนี้จะปิดเสียงข้อผิดพลาดของคอมไพเลอร์ แต่ตามที่เราได้อภิปรายไปแล้ว มันจะเสียสละความปลอดภัยของประเภททั้งหมดสำหรับไลบรารีนั้น มันเป็นการแก้ไขชั่วคราว ไม่ใช่การแก้ปัญหาระยะยาว
การสร้างไฟล์ประกาศแบบกำหนดเองขั้นพื้นฐาน
แนวทางที่ดีกว่าคือการเริ่มกำหนดประเภทสำหรับส่วนต่างๆ ของไลบรารีที่คุณใช้งานจริง สมมติว่าเรามีไลบรารีอย่างง่ายชื่อ `string-utils` ที่ส่งออกฟังก์ชันเดียว
// ใน node_modules/string-utils/index.js
module.exports.capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
เราสามารถสร้างไฟล์ string-utils.d.ts ในไดเร็กทอรี `types` ที่กำหนดไว้ในรากของโปรเจกต์ของเรา
// ใน my-project/types/string-utils.d.ts
declare module 'string-utils' {
export function capitalize(str: string): string;
// คุณสามารถเพิ่มคำจำกัดความฟังก์ชันอื่นๆ ที่นี่เมื่อคุณใช้งาน
// export function slugify(str: string): string;
}
ตอนนี้ เราต้องบอก TypeScript ว่าจะหาคำจำกัดความประเภทแบบกำหนดเองของเราได้ที่ไหน เราทำสิ่งนี้ใน tsconfig.json:
{
"compilerOptions": {
// ... ตัวเลือกอื่นๆ
"baseUrl": ".",
"paths": {
"*": ["types/*"]
}
}
}
ด้วยการตั้งค่านี้ เมื่อคุณ import { capitalize } from 'string-utils' TypeScript จะพบไฟล์ประกาศแบบกำหนดเองของคุณและให้ความปลอดภัยของประเภทที่คุณกำหนด คุณสามารถค่อยๆ สร้างไฟล์นี้ขึ้นมาเมื่อคุณใช้ฟีเจอร์เพิ่มเติมของไลบรารี
เจาะลึก: การเขียนไฟล์ประกาศ
มาสำรวจแนวคิดขั้นสูงบางอย่างที่คุณจะพบเมื่อเขียนหรืออ่านไฟล์ประกาศ
การประกาศชนิดของการส่งออกที่แตกต่างกัน
โมดูล JavaScript สามารถส่งออกสิ่งต่างๆ ได้หลายวิธี ไฟล์ประกาศของคุณต้องตรงกับโครงสร้างการส่งออกของไลบรารี
- Named Exports: นี่เป็นเรื่องปกติที่สุด เราเห็นมันข้างต้นด้วย `export function capitalize(...)` คุณยังสามารถส่งออกค่าคงที่ อินเทอร์เฟซ และคลาส
- Default Export: สำหรับไลบรารีที่ใช้ `export default`
- UMD Globals: สำหรับไลบรารีเก่าที่ออกแบบมาเพื่อทำงานในเบราว์เซอร์ผ่านแท็ก
<script>พวกเขามักจะแนบตัวเองเข้ากับอ็อบเจกต์ส่วนกลาง `window` คุณสามารถประกาศตัวแปรส่วนกลางเหล่านี้ได้ export =และimport = require(): ไวยากรณ์นี้สำหรับโมดูล CommonJS เก่าที่ใช้ `module.exports = ...` ตัวอย่างเช่น หากไลบรารีทำ `module.exports = myClass;`
declare module 'my-lib' {
export const version: string;
export interface Options { retries: number; }
export function doSomething(options: Options): Promise
declare module 'my-default-lib' {
// สำหรับการส่งออกค่าเริ่มต้นของฟังก์ชัน
export default function myCoolFunction(): void;
// สำหรับการส่งออกค่าเริ่มต้นของอ็อบเจกต์
// const myLib = { name: 'lib', version: '1.0' };
// export default myLib;
}
// ประกาศตัวแปรส่วนกลาง '$' ของประเภทที่ระบุ
declare var $: JQueryStatic;
// ใน my-class.d.ts
declare class MyClass { constructor(name: string); }
export = MyClass;
// ใน app.ts ของคุณ
import MyClass = require('my-class');
const instance = new MyClass('test');
แม้ว่าจะไม่ค่อยพบบ่อยนักกับ ES Modules สมัยใหม่ แต่สิ่งนี้สำคัญอย่างยิ่งต่อความเข้ากันได้กับแพ็กเกจ Node.js เก่าที่ยังคงใช้งานอย่างแพร่หลาย
Module Augmentation: การขยายประเภทที่มีอยู่
หนึ่งในคุณสมบัติที่ทรงพลังที่สุดคือ module augmentation (หรือที่เรียกว่า declaration merging) สิ่งนี้ช่วยให้คุณสามารถเพิ่มคุณสมบัติให้กับอินเทอร์เฟซที่มีอยู่ซึ่งกำหนดไว้ในไฟล์ประกาศของแพ็กเกจอื่น สิ่งนี้มีประโยชน์อย่างยิ่งสำหรับไลบรารีที่มีสถาปัตยกรรมปลั๊กอิน เช่น Express หรือ Fastify
สมมติว่าคุณกำลังใช้ middleware ใน Express ที่เพิ่มคุณสมบัติ `user` ให้กับอ็อบเจกต์ `Request` หากไม่มี augmentation TypeScript จะแจ้งว่า `user` ไม่มีอยู่ใน `Request`
นี่คือวิธีที่คุณสามารถบอก TypeScript เกี่ยวกับคุณสมบัตินี้:
// ในไฟล์ types/express.d.ts ของคุณ
// เราต้องนำเข้าประเภทดั้งเดิมเพื่อเพิ่มเข้าไป
import { UserProfile } from './auth'; // สมมติว่าคุณมีประเภท UserProfile
// บอก TypeScript ว่าเรากำลังเพิ่มให้กับโมดูล 'express-serve-static-core'
declare module 'express-serve-static-core' {
// กำหนดเป้าหมายอินเทอร์เฟซ 'Request' ภายในโมดูลนั้น
interface Request {
// เพิ่มคุณสมบัติที่กำหนดเองของเรา
user?: UserProfile;
}
}
ตอนนี้ ทั่วทั้งแอปพลิเคชันของคุณ อ็อบเจกต์ Express `Request` จะถูกพิมพ์อย่างถูกต้องด้วยคุณสมบัติ `user` ที่เป็นทางเลือก และคุณจะได้รับความปลอดภัยของประเภทและการเติมข้ออัตโนมัติเต็มรูปแบบ
Triple-Slash Directives
บางครั้งคุณอาจเห็นความคิดเห็นที่ส่วนบนของไฟล์ .d.ts ซึ่งขึ้นต้นด้วยสามเครื่องหมายทับ (///) เหล่านี้คือ triple-slash directives ซึ่งทำหน้าที่เป็นคำสั่งของคอมไพเลอร์
/// <reference types="..." />: นี่คือที่พบบ่อยที่สุด มันรวมคำจำกัดความประเภทของแพ็กเกจอื่นอย่างชัดเจนว่าเป็น dependency ตัวอย่างเช่น ประเภทสำหรับปลั๊กอิน WebdriverIO อาจมี/// <reference types="webdriverio" />เนื่องจากประเภทของตนเองขึ้นอยู่กับประเภท WebdriverIO หลัก/// <reference path="..." />: สิ่งนี้ใช้เพื่อประกาศ dependency กับไฟล์อื่นภายในโปรเจกต์เดียวกัน เป็นไวยากรณ์เก่า ซึ่งส่วนใหญ่ถูกแทนที่ด้วย import ของ ES module
แนวทางปฏิบัติที่ดีที่สุดสำหรับการจัดการไฟล์ประกาศ
- ชอบประเภทที่รวมไว้: เมื่อเลือกระหว่างไลบรารี ให้เลือกไลบรารีที่เขียนด้วย TypeScript หรือรวมคำจำกัดความประเภทอย่างเป็นทางการไว้ สิ่งนี้บ่งชี้ถึงความมุ่งมั่นต่อระบบนิเวศของ TypeScript
- เก็บ
@typesไว้ในdevDependencies: ติดตั้งแพ็กเกจ@typesด้วย--save-devหรือ-Dเสมอ ไม่จำเป็นต้องใช้กับโค้ดการผลิตของคุณ - ปรับเวอร์ชันให้ตรงกัน: แหล่งที่มาของข้อผิดพลาดที่พบบ่อยคือความไม่ตรงกันระหว่างเวอร์ชันของไลบรารีและเวอร์ชัน
@typesการอัปเกรดเวอร์ชันหลักในไลบรารี (เช่น จาก v2 เป็น v3) น่าจะมีการเปลี่ยนแปลงที่เข้ากันไม่ได้ใน API ซึ่งต้องสะท้อนในแพ็กเกจ@typesพยายามทำให้พวกมันสอดคล้องกัน - ใช้
tsconfig.jsonเพื่อควบคุม: ตัวเลือกคอมไพเลอร์typeRootsและtypesในtsconfig.jsonของคุณสามารถให้การควบคุมแบบละเอียดแก่คุณเกี่ยวกับตำแหน่งที่ TypeScript ค้นหาไฟล์ประกาศtypeRootsจะบอกคอมไพเลอร์ว่าโฟลเดอร์ใดที่ต้องตรวจสอบ (โดยค่าเริ่มต้นคือ./node_modules/@types) และtypesช่วยให้คุณระบุรายการแพ็กเกจประเภทที่จะรวมได้อย่างชัดเจน - มีส่วนร่วมกลับ: หากคุณเขียนไฟล์ประกาศที่ครอบคลุมสำหรับไลบรารีที่ไม่มี ให้พิจารณาที่จะมีส่วนร่วมในโครงการ DefinitelyTyped นี่เป็นวิธีที่ยอดเยี่ยมในการตอบแทนชุมชนนักพัฒนาทั่วโลกและช่วยเหลือผู้อื่นนับพัน
บทสรุป: ฮีโร่ผู้อยู่เบื้องหลังความปลอดภัยของประเภท
ไฟล์ประกาศ TypeScript คือฮีโร่ผู้อยู่เบื้องหลังที่ทำให้สามารถผสานรวมโลกแบบไดนามิกและแผ่ขยายของ JavaScript เข้ากับสภาพแวดล้อมการพัฒนาที่แข็งแกร่งและปลอดภัยประเภทได้อย่างราบรื่น พวกมันคือลิงก์สำคัญที่ช่วยเพิ่มขีดความสามารถให้กับเครื่องมือของเรา ป้องกันข้อบกพร่องนับไม่ถ้วน และทำให้ฐานโค้ดของเรามีความยืดหยุ่นและสามารถเขียนเอกสารด้วยตนเองได้มากขึ้น
ด้วยการทำความเข้าใจวิธีการค้นหา ใช้งาน และแม้กระทั่งสร้างไฟล์ .d.ts ของคุณเอง คุณไม่ได้เพียงแค่แก้ไขข้อผิดพลาดของคอมไพเลอร์เท่านั้น คุณกำลังยกระดับเวิร์กโฟลว์การพัฒนาทั้งหมดของคุณ คุณกำลังปลดล็อกศักยภาพสูงสุดของทั้ง TypeScript และระบบนิเวศที่หลากหลายของไลบรารี JavaScript สร้างการทำงานร่วมกันที่มีประสิทธิภาพซึ่งส่งผลให้ซอฟต์แวร์ดีขึ้นและเชื่อถือได้มากขึ้นสำหรับผู้ชมทั่วโลก